package com.jxj.study.util.ImageTests;

import com.jxj.study.util.ImageIdentify;
import org.apache.commons.lang3.StringUtils;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 批量图片截取工具主类
 * 功能：选择文件夹浏览图片，可截取选定区域，识别文本并保存
 * 选择某个文件夹，对文件夹中的图片进行遍历预览，可选择对其中的图片进行截图，截图后自动保存到当前目录下新创建的文件夹中，
 * 新建的文件夹命名调用一个函数生成，入参是截图的输入流，并将源文件复制一份同截图部分保存到同一个文件夹，每一个图片操作保存独立的文件夹。
 * 并可以对复制的源文件和截图的图片命名，并把文件夹存在当前文件夹当天为文件名的文件夹下，列如20250408这样的名字下，如果没有就创建这个文件夹
 */
public class BatchImageCropper extends JFrame {

    // ==================== 常量定义 ====================
    private static final int RESIZE_HANDLE_SIZE = 10; // 选择框调整手柄大小
    private static final int MIN_SELECTION_WIDTH = 10; // 选择框最小宽度
    private static final int MIN_SELECTION_HEIGHT = 10; // 选择框最小高度
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd"); // 日期格式
    private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HHmmss"); // 时间格式

    // ==================== 组件定义 ====================
    private BufferedImage currentImage;      // 当前显示的图片
    private JLabel imageLabel;              // 图片显示标签
    private JPanel panel;              // 图片面板
    private JLabel statusLabel;             // 状态栏标签
    private JTextArea textArea;             // 文本编辑区域
    private JScrollPane textScrollPane;     // 文本区域滚动条

    // ==================== 状态变量 ====================
    // 选择框位置和尺寸
    private final Rectangle selectionRect = new Rectangle(100, 100, 200, 150);
    private Point dragStart;                // 拖拽起始点
    private boolean isDragging = false;     // 是否正在拖拽
    private boolean isResizing = false;     // 是否正在调整大小
    private List<File> imageFiles;         // 图片文件列表
    private int currentIndex = -1;         // 当前图片索引
    private File currentDirectory;         // 当前目录
    private String defaultFolderPath; // 默认文件打开路径

    /**
     * 构造函数：初始化界面
     */
    public BatchImageCropper() {
        // 初始化窗口基本属性
        setTitle("批量图片截取工具");
        setSize(1000, 800);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());

        // 初始化UI组件
        initToolbar();
        initImageDisplay();
        initImageJPanel();
        initTextArea();
        loadConfig(); // 加载配置文件

        // 设置主布局
        setupMainLayout();
    }

    /**
     * 初始化工具栏
     */
    private void initToolbar() {
        JPanel toolbar = new JPanel(new FlowLayout(FlowLayout.LEFT));

        // 功能按钮
        JButton openButton = new JButton("选择文件夹");
        JButton prevButton = new JButton("上一张");
        JButton nextButton = new JButton("下一张");
        JButton cropButton = new JButton("截取并保存");

        // 添加组件
        toolbar.add(openButton);
        toolbar.add(prevButton);
        toolbar.add(nextButton);
        toolbar.add(cropButton);
        // 状态标签
        statusLabel = new JLabel("请选择包含图片的文件夹");
        toolbar.add(statusLabel);

        // 添加事件监听
        openButton.addActionListener(e -> openFolder());
        prevButton.addActionListener(e -> showPreviousImage());
        nextButton.addActionListener(e -> showNextImage());
        cropButton.addActionListener(e -> cropAndSave(selectionRect));
        add(toolbar, BorderLayout.NORTH);
    }

    private void loadConfig() {
        Properties prop = new Properties();
        try (FileInputStream input = new FileInputStream("config.properties")) {
            prop.load(new InputStreamReader(input, StandardCharsets.UTF_8));
            defaultFolderPath = prop.getProperty("default.folder", System.getProperty("user.home"));
        } catch (IOException ex) {
            defaultFolderPath = System.getProperty("user.home"); // 默认用户目录
        }
    }

    /**
     * 初始化图片显示区域
     */
    private void initImageDisplay() {
        imageLabel = new JLabel() {
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                // 绘制图片
                if (currentImage != null) {
                    g.drawImage(currentImage, 0, 0, this);
                }

                // 绘制选择框
                if (selectionRect != null) {
                    Graphics2D g2d = (Graphics2D) g.create();

                    // 绘制虚线框
                    g2d.setColor(Color.RED);
                    g2d.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT,
                            BasicStroke.JOIN_BEVEL, 0, new float[]{5}, 0));
                    g2d.drawRect(selectionRect.x, selectionRect.y,
                            selectionRect.width, selectionRect.height);

                    // 绘制调整手柄
                    g2d.setColor(Color.BLUE);
                    g2d.fillRect(
                            selectionRect.x + selectionRect.width - RESIZE_HANDLE_SIZE / 2,
                            selectionRect.y + selectionRect.height - RESIZE_HANDLE_SIZE / 2,
                            RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE);

                    g2d.dispose();
                }
            }
        };

        // 鼠标监听器 - 处理选择框拖拽和调整
        imageLabel.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                if (currentImage == null) return;

                Point mousePoint = e.getPoint();
                Point resizeHandle = new Point(selectionRect.x + selectionRect.width,
                        selectionRect.y + selectionRect.height);

                // 检查是否点击了调整手柄
                if (mousePoint.distance(resizeHandle) < RESIZE_HANDLE_SIZE) {
                    isResizing = true;
                }
                // 检查是否点击了选择框内部
                else if (selectionRect.contains(mousePoint)) {
                    isDragging = true;
                    dragStart = mousePoint;
                }
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                isDragging = false;
                isResizing = false;
            }
        });

        // 鼠标移动监听器 - 处理拖拽和调整
        imageLabel.addMouseMotionListener(new MouseAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                if (currentImage == null) return;

                Point currentPoint = e.getPoint();

                // 处理拖拽移动
                if (isDragging) {
                    int dx = currentPoint.x - dragStart.x;
                    int dy = currentPoint.y - dragStart.y;

                    selectionRect.x += dx;
                    selectionRect.y += dy;

                    // 限制在图片范围内
                    selectionRect.x = Math.max(0, Math.min(selectionRect.x,
                            imageLabel.getWidth() - selectionRect.width));
                    selectionRect.y = Math.max(0, Math.min(selectionRect.y,
                            imageLabel.getHeight() - selectionRect.height));

                    dragStart = currentPoint;
                } else if (isResizing) { // 处理调整大小
                    int newWidth = currentPoint.x - selectionRect.x;
                    int newHeight = currentPoint.y - selectionRect.y;

                    // 应用最小尺寸限制
                    selectionRect.width = Math.max(MIN_SELECTION_WIDTH, newWidth);
                    selectionRect.height = Math.max(MIN_SELECTION_HEIGHT, newHeight);

                    // 限制在图片范围内
                    selectionRect.width = Math.min(selectionRect.width, imageLabel.getWidth() - selectionRect.x);
                    selectionRect.height = Math.min(selectionRect.height, imageLabel.getHeight() - selectionRect.y);
                }

                imageLabel.repaint();
            }
        });
    }

    /**
     * 初始化图片显示区域
     */
    private Point startPoint;
    private Point endPoint;
    private Rectangle selection;
    private boolean drawing = false;
    private double scale = 1.0;
    private void initImageJPanel() {
        panel = new JPanel() {
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                // 绘制图片
                if (currentImage != null) {
                    Graphics2D g2 = (Graphics2D)g;
                    g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                            RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                    int w = (int)(currentImage.getWidth() * scale);
                    int h = (int)(currentImage.getHeight() * scale);
                    g2.drawImage(currentImage, 0, 0, w, h, null);

//                    if (drawing && selection != null) {
//                        g2.setColor(Color.RED);
//                        g2.drawRect(selection.x, selection.y,
//                                selection.width, selection.height);
//                    }
                    if (drawing && startPoint != null && endPoint != null) {
                        g.drawRect(Math.min(startPoint.x, endPoint.x), Math.min(startPoint.y, endPoint.y),
                                Math.abs(endPoint.x - startPoint.x), Math.abs(endPoint.y - startPoint.y));
                    }
                }
//                if (drawing && startPoint != null && endPoint != null) {
//                    g.drawRect(Math.min(startPoint.x, endPoint.x), Math.min(startPoint.y, endPoint.y),
//                            Math.abs(endPoint.x - startPoint.x), Math.abs(endPoint.y - startPoint.y));
//                }
            }
        };

        panel.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                //鼠标监听事件
                panel.requestFocusInWindow();
                startPoint = e.getPoint();
                drawing = true;
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                endPoint = e.getPoint();
                drawing = false;
                selection = new Rectangle(startPoint.x, startPoint.y,
                        endPoint.x-startPoint.x, endPoint.y-startPoint.y);
                startPoint = null;
//                    repaint();
            }
        });

        panel.addMouseMotionListener(new MouseAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                endPoint = e.getPoint();
                selection = new Rectangle(startPoint.x, startPoint.y,
                        endPoint.x-startPoint.x, endPoint.y-startPoint.y);
                repaint();  // 在拖动时实时重绘面板以更新选择框的显示
            }
        });

        panel.addMouseWheelListener(e -> {
            int rotation = e.getWheelRotation();
            scale *= rotation < 0 ? 1.1 : 0.9;
            scale = Math.max(0.1, Math.min(10.0, scale));
            repaint();
        });

        panel.setFocusable(true);
        panel.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (currentImage == null) return;
                if (e.getKeyCode() == KeyEvent.VK_C) { // C键 - 截图

                    int x = Math.max(0, Math.min((int)(selection.x / scale), currentImage.getWidth()));
                    int y = Math.max(0, Math.min((int)(selection.y / scale), currentImage.getHeight()));
                    int w = Math.max(0, Math.min((int)(selection.width / scale), currentImage.getWidth() - x));
                    int h = Math.max(0, Math.min((int)(selection.height / scale), currentImage.getHeight() - y));
                    Rectangle rectangle = new Rectangle(x, y,w, h);
                    try {
                        BufferedImage originalImage = ImageIO.read(imageFiles.get(currentIndex));
                        String recognizedText = ImageIdentify.recognizeImageText(originalImage,rectangle);
                        if (StringUtils.isNotBlank(recognizedText)) {
                            textArea.append("\n" + recognizedText);
                        }
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
//                    cropAndSave(rectangle);
                    e.consume();
                }
            }
        });
    }

    /**
     * 初始化文本区域
     */
    private void initTextArea() {
        textArea = new JTextArea(5, 20);
        textArea.setLineWrap(true); // 自动换行
        textArea.setWrapStyleWord(true); // 按单词换行
        textScrollPane = new JScrollPane(textArea);

        // 创建并应用DocumentFilter 过滤指定输入
        DocumentFilter filter = new DocumentFilter() {
            @Override
            public void insertString(FilterBypass fb, int offset,
                                     String string, AttributeSet attr) throws BadLocationException {
                StringBuilder sb = new StringBuilder(string);
                for (int i = 0; i < sb.length(); i++) {
                    char c = sb.charAt(i);
                    if (Character.toString(c).matches("[abc]")) { // 示例：禁止输入a, b, c
                        return; // 禁止插入该字符
                    }
                }
                super.insertString(fb, offset, sb.toString(), attr);
            }

            @Override
            public void replace(FilterBypass fb, int offset, int length,
                                String text, AttributeSet attrs) throws BadLocationException {
                StringBuilder sb = new StringBuilder(text);
                for (int i = 0; i < sb.length(); i++) {
                    char c = sb.charAt(i);
                    if (Character.toString(c).matches("[abc]")) { // 示例：禁止输入a, b, c
                        return; // 禁止替换为该字符
                    }
                }
                super.replace(fb, offset, length, sb.toString(), attrs);
            }
        };
        ((AbstractDocument) textArea.getDocument()).setDocumentFilter(filter);

        // 键盘监听器 - 处理S和Z键
        textArea.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (currentImage == null) return;

                switch (e.getKeyCode()) {
                    case KeyEvent.VK_S: // S键 - 追加文本
                        appendRecognizedText(false);
                        e.consume();
                        break;
                    case KeyEvent.VK_Z: // Z键 - 换行追加
                        appendRecognizedText(true);
                        e.consume();
                        break;
                }
            }
        });
    }

    /**
     * 设置主界面布局
     */
    private void setupMainLayout() {
        // 右侧面板（文本区域和帮助信息）
        JPanel rightPanel = new JPanel(new BorderLayout());
        rightPanel.add(textScrollPane, BorderLayout.CENTER);

        // 帮助信息面板
        JPanel helpPanel = new JPanel(new GridLayout(4, 1));
        helpPanel.add(new JLabel("快捷键:"));
        helpPanel.add(new JLabel("S - 追加文本"));
        helpPanel.add(new JLabel("Z - 换行追加"));
        helpPanel.add(new JLabel("文本会自动保存到输出文件夹"));
        rightPanel.add(helpPanel, BorderLayout.SOUTH);

        JSplitPane jSplitPane2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
                new JScrollPane(imageLabel), new JScrollPane(panel));
        jSplitPane2.setDividerLocation(350);
        // 主分割面板
        JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, jSplitPane2, rightPanel);
        splitPane.setDividerLocation(700); // 设置分割位置
        add(splitPane, BorderLayout.CENTER);
    }

    // ==================== 核心功能方法 ====================

    /**
     * 打开文件夹并加载图片
     */
    private void openFolder() {
        JFileChooser fileChooser = new JFileChooser();
        fileChooser.setDialogTitle("选择图片文件夹");
        fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

        // 设置默认目录（新增）
        if (new File(defaultFolderPath).exists()) {
            fileChooser.setCurrentDirectory(new File(defaultFolderPath));
        }

        if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            currentDirectory = fileChooser.getSelectedFile();
            saveLastUsedFolder(); // 保存最后使用的文件夹（新增）
            loadImageFiles();

            if (!imageFiles.isEmpty()) {
                currentIndex = 0;
                loadCurrentImage();
            } else {
                statusLabel.setText("文件夹中没有图片文件");
            }
        }
    }

    // 新增方法：保存最后使用的文件夹
    private void saveLastUsedFolder() {
        try (FileOutputStream output = new FileOutputStream("config.properties")) {
            Properties prop = new Properties();
            prop.setProperty("default.folder", currentDirectory.getAbsolutePath());
            prop.store(output, "Last used image folder");
        } catch (IOException ex) {
            System.err.println("无法保存配置: " + ex.getMessage());
        }
    }

    private void loadImageFiles() {
        imageFiles = Arrays.stream(Objects.requireNonNull(currentDirectory.listFiles()))
                .filter(file -> {
                    String name = file.getName().toLowerCase();
                    return name.endsWith(".jpg") || name.endsWith(".jpeg")
                            || name.endsWith(".png") || name.endsWith(".gif")
                            || name.endsWith(".bmp");
                })
                .sorted(Comparator.comparing(File::getName))
                .collect(Collectors.toList());

        statusLabel.setText(String.format("已加载 %d 张图片", imageFiles.size()));
    }

    /**
     * 加载当前索引对应的图片
     */
    private void loadCurrentImage() {
        if (currentIndex >= 0 && currentIndex < imageFiles.size()) {
            try {
                currentImage = ImageIO.read(imageFiles.get(currentIndex));
                scaleImageToFit();
                statusLabel.setText(String.format("图片 %d/%d: %s",
                        currentIndex + 1, imageFiles.size(),
                        imageFiles.get(currentIndex).getName()));
            } catch (IOException ex) {
                JOptionPane.showMessageDialog(this, "无法加载图片: " + ex.getMessage(),
                        "错误", JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    /**
     * 缩放图片以适应显示区域
     */
    private void scaleImageToFit() {
        if (currentImage == null) return;

        int labelWidth = imageLabel.getWidth();
        int labelHeight = imageLabel.getHeight();

        if (labelWidth <= 0 || labelHeight <= 0) {
            imageLabel.setIcon(new ImageIcon(currentImage));
            return;
        }

        // 计算缩放比例
        double widthRatio = (double) labelWidth / currentImage.getWidth();
        double heightRatio = (double) labelHeight / currentImage.getHeight();
        double scale = Math.min(widthRatio, heightRatio);

        // 创建缩放后的图片
        int scaledWidth = (int) (currentImage.getWidth() * scale);
        int scaledHeight = (int) (currentImage.getHeight() * scale);

        BufferedImage scaledImage = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = scaledImage.createGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.drawImage(currentImage, 0, 0, scaledWidth, scaledHeight, null);
        g2d.dispose();

        currentImage = scaledImage;
        imageLabel.setPreferredSize(new Dimension(scaledWidth, scaledHeight));
        revalidate();
        repaint();
    }

    /**
     * 显示上一张图片
     */
    private void showPreviousImage() {
        if (imageFiles == null || imageFiles.isEmpty()) return;
        
        currentIndex = (currentIndex - 1 + imageFiles.size()) % imageFiles.size();
        loadCurrentImage();
    }

    /**
     * 显示下一张图片
     */
    private void showNextImage() {
        if (imageFiles == null || imageFiles.isEmpty()) return;
        
        currentIndex = (currentIndex + 1) % imageFiles.size();
        loadCurrentImage();
    }

    /**
     * 追加识别文本到文本区域
     *
     * @param newLine 是否换行追加
     */
    private void appendRecognizedText(boolean newLine) {
        // 获取选择区域（如果没选择则用整个图片）
        BufferedImage subImage;
        if (selectionRect != null && selectionRect.width > 0 && selectionRect.height > 0) {
            subImage = currentImage.getSubimage(selectionRect.x, selectionRect.y,
                    selectionRect.width, selectionRect.height);
        } else {
            subImage = currentImage;
        }

        // 调用识别函数（此处为模拟）
        // 最后识别的文本
        String lastRecognizedText = recognizeTextFromImage(subImage);

        // 追加到文本区域
        if (newLine) {
            textArea.append("\n" + lastRecognizedText);
        } else {
            textArea.append(lastRecognizedText);
        }
    }

    /**
     * 模拟OCR识别功能（实际应用中应替换为真实OCR接口）
     *
     * @param image 要识别的图片区域
     * @return 识别结果文本
     */
    private String recognizeTextFromImage(BufferedImage image) {
        Rectangle rect = selectionRect != null ? selectionRect :
                new Rectangle(0, 0, image.getWidth(), image.getHeight());

        return String.format("[识别结果 位置(%d,%d) 尺寸(%dx%d) 时间%s]",
                rect.x, rect.y, rect.width, rect.height,
                new SimpleDateFormat("HH:mm:ss").format(new Date()));
    }

    /**
     * 执行截图并保存
     */
    private void cropAndSave(Rectangle rectangle) {
        if (currentImage == null || imageFiles == null || currentIndex < 0) {
            JOptionPane.showMessageDialog(this, "没有可处理的图片", "提示"
                    , JOptionPane.WARNING_MESSAGE);
            return;
        }

        try {
            // 创建日期文件夹
            File dateFolder = new File(currentDirectory, DATE_FORMAT.format(new Date()));
            if (!dateFolder.exists() && !dateFolder.mkdir()) {
                JOptionPane.showMessageDialog(this, "无法创建日期文件夹",
                        "错误", JOptionPane.ERROR_MESSAGE);
                return;
            }

            // 创建操作文件夹
            String folderName = generateFolderName(currentImage);
            File outputFolder = new File(dateFolder, folderName);
            if (!outputFolder.mkdir()) {
                JOptionPane.showMessageDialog(this, "无法创建输出文件夹",
                        "错误", JOptionPane.ERROR_MESSAGE);
                return;
            }

            // 生成文件名
            String timestamp = TIME_FORMAT.format(new Date());
            String originalFilename = "original_" + timestamp + getFileExtension(imageFiles.get(currentIndex).getName());
            String croppedFilename = "cropped_" + timestamp + ".png";
            
            // 保存原始图片副本
            Files.copy(imageFiles.get(currentIndex).toPath(), new File(outputFolder, originalFilename).toPath(),
                    StandardCopyOption.REPLACE_EXISTING
            );

            //保存截图
            BufferedImage croppedImage = getCroppedImage(rectangle);
            ImageIO.write(croppedImage, "png", new File(outputFolder, croppedFilename));

            // 保存文本内容
            if (!textArea.getText().isEmpty()) {
                Files.write(new File(outputFolder, "recognized_text.txt").toPath(), textArea.getText().getBytes());
            }

            JOptionPane.showMessageDialog(this, String.format("保存成功到: %s", outputFolder.getPath()),
                    "成功", JOptionPane.INFORMATION_MESSAGE);
            
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(this, "处理失败: " + ex.getMessage(), 
                    "错误", JOptionPane.ERROR_MESSAGE);
        }
    }

    /**
     * 获取当前选择区域的截图
     *
     * @return 截取的图片
     */
    private BufferedImage getCroppedImage(Rectangle rectangle) throws IOException {
        // 读取原始图片（非缩放版本）
        BufferedImage originalImage = ImageIO.read(imageFiles.get(currentIndex));

        // 计算原始图片上的对应区域
        double scaleX = (double) originalImage.getWidth() / currentImage.getWidth();
        double scaleY = (double) originalImage.getHeight() / currentImage.getHeight();

        int x = (int) (rectangle.x * scaleX);
        int y = (int) (rectangle.y * scaleY);
        int width = (int) (rectangle.width * scaleX);
        int height = (int) (rectangle.height * scaleY);

        // 确保不超出图片范围
        x = Math.max(0, Math.min(x, originalImage.getWidth() - 1));
        y = Math.max(0, Math.min(y, originalImage.getHeight() - 1));
        width = Math.max(1, Math.min(width, originalImage.getWidth() - x));
        height = Math.max(1, Math.min(height, originalImage.getHeight() - y));

        return originalImage.getSubimage(x, y, width, height);
    }

    /**
     * 生成输出文件夹名称
     *
     * @param image 当前图片
     * @return 生成的文件夹名称
     */
    private String generateFolderName(BufferedImage image) {
        return String.format("crop_%dx%d_%s",
                selectionRect != null ? selectionRect.width : image.getWidth(),
                selectionRect != null ? selectionRect.height : image.getHeight(),
                TIME_FORMAT.format(new Date()));
    }

    /**
     * 获取文件扩展名
     *
     * @param filename 文件名
     * @return 文件扩展名
     */
    private String getFileExtension(String filename) {
        int dotIndex = filename.lastIndexOf('.');
        return (dotIndex == -1) ? "" : filename.substring(dotIndex);
    }

    /**
     * 程序入口
     */
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            BatchImageCropper app = new BatchImageCropper();
            app.setVisible(true);
        });
    }
}